﻿using log4net;
using log4net.Appender;
using log4net.Config;
using log4net.Core;
using log4net.Repository.Hierarchy;
using System;
using System.Windows.Forms;

[assembly: XmlConfigurator(ConfigFile = "Log4Net.config", Watch = true)]

namespace ShareHelper
{
    /// <summary>
    /// 输出日志类
    /// </summary>
    public class Log : AppenderSkeleton
    {
        private static Logger Root { get; }

        /// <summary>
        /// 写出日志等级此等级一下日志无法写出
        /// </summary>
        public static Level LogLevel { get { return Root.Level; } set { Root.Level = value; } }

        static Log()
        {
            Root = ((Hierarchy)LogManager.GetRepository()).Root;
        }

        /// <summary>
        /// 默认为写出日志等级
        /// </summary>
        public Level Level { get; set; }

        /// <summary>
        /// 日志输出组建
        /// </summary>
        private TextBox TextBox { get; }

        /// <summary>
        /// 创建一个 TexBox 输出日志类
        /// </summary>
        /// <param name="textBox">日志输出组件</param>
        /// <param name="Record">是否加入到静态输出系统</param>
        public Log(TextBox textBox, bool Record = true)
        {
            Name = typeof(Log).Name;
            textBox.ReadOnly = true;
            TextBox = textBox;
            Level = LogLevel;
            if (Record) BasicConfigurator.Configure(this);
        }

        /// <summary>
        ///兼容之前的日志输出 推荐生成后直接使用静态输出
        /// </summary>
        /// <param name="text"></param>
        public void LabelText(string text)
        {
            LabelText(Level.Off, text);
        }

        /// <summary>
        /// 输出日志
        ///  <param name="level"></param>
        /// </summary>
        /// <param name="text">输出内容</param>

        private void LabelText(Level level, string text)
        {
            if (Level > level) return;
            LabelText(this, text);
        }

        /// <summary>
        /// Debug 日志输出记录
        /// </summary>
        /// <param name="text"></param>
        /// <param name="exception"></param>
        public static void Debug(string text, Exception exception = null) => WriteLine(Level.Debug, text, exception);

        /// <summary>
        /// Info 日志输出记录
        /// </summary>
        /// <param name="text"></param>
        /// <param name="exception"></param>
        public static void Info(string text, Exception exception = null) => WriteLine(Level.Info, text, exception);

        /// <summary>
        /// Warn 日志输出记录
        /// </summary>
        /// <param name="text"></param>
        /// <param name="exception"></param>
        public static void Warn(string text, Exception exception = null) => WriteLine(Level.Warn, text, exception);

        /// <summary>
        /// Error 日志输出记录
        /// </summary>
        /// <param name="text"></param>
        /// <param name="exception"></param>
        public static void Error(string text, Exception exception = null) => WriteLine(Level.Error, text, exception);

        /// <summary>
        /// Fatal 日志输出记录
        /// </summary>
        /// <param name="text"></param>
        /// <param name="exception"></param>
        public static void Fatal(string text, Exception exception = null) => WriteLine(Level.Fatal, text, exception);

        /// <summary>
        ///输出内部日志
        /// </summary>
        /// <param name="level"></param>
        /// <param name="text"></param>
        /// <param name="exception"></param>

        private static void WriteLine(Level level, string text, Exception exception = null)
        {
            if (LogLevel > level)//判断是否全局写出
            {
                //不是全局写出则 尝试窗口打印输出
                var log = Root.GetAppender(typeof(Log).Name);
                if (log is Log log1) log1.LabelText(level, text);
            }
            else//全局输出则全局输出 全局输出包括窗口输出
                Root.Log(typeof(Log), level, text, exception);
        }

        /// <summary>
        /// 执行实际的日志记录。
        /// </summary>
        /// <param name="loggingEvent"></param>
        protected override void Append(LoggingEvent loggingEvent) => LabelText(loggingEvent.Level, loggingEvent.RenderedMessage);

        /// <summary>
        /// 输出日志
        /// </summary>
        /// <param name="log"></param>
        /// <param name="text"></param>
        private static void LabelText(Log log, string text)
        {
            if (string.IsNullOrEmpty(text)) return;
            log.BeginInvoke(new Action<string>((a) =>
            {
                // 记录原选择
                var selstart = log.TextBox.SelectionStart;
                var sellen = log.TextBox.SelectionLength;
                log.TextBox.AppendText(a);
                if (sellen > 0)
                {
                    if (selstart < log.TextBox.TextLength) // 恢复选择
                    {
                        sellen = Math.Min(sellen, log.TextBox.TextLength - selstart - 1);
                        log.TextBox.Select(selstart, sellen);
                        log.TextBox.ScrollToCaret();
                    }
                }
            }), false, $"{TimerEx.Now:HH:mm:ss}:{text}\r\n");
        }

        /// <summary>
        /// 在创建控件的基础句柄所在线程上，用指定的自变量异步执行指定委托。
        /// </summary>
        /// <param name="method">一个方法委托，它采用的参数的数量和类型与 args 参数中所包含的相同。</param>
        /// <param name="isWait">是否等待委托事件执行完毕</param>
        /// <param name="args">作为给定方法的参数传递的对象数组。 如果不需要自变量，则可以为 null。</param>
        public void BeginInvoke(Delegate method, bool isWait = false, params object[] args)
        {
            TextBox.BeginInvokeEx(method, isWait, args);
        }
    }
}